﻿/*******************************************************
 * 
 * 作者：胡庆访
 * 创建时间：20130326 16:08
 * 说明：此文件只包含一个类，具体内容见类型注释。
 * 运行环境：.NET 4.0
 * 版本号：1.0.0
 * 
 * 历史记录：
 * 创建文件 胡庆访 20130326 16:08
 * 
*******************************************************/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using hxy;

namespace MSDNConverter
{
    public class MSDNReader
    {
        private WebClient _client = new WebClient { Encoding = Encoding.UTF8 };

        protected WebClient Client
        {
            get { return _client; }
        }

        private int _totalCount = 0;

        private int _currentContentIndex = 0;

        public MSDNReader()
        {
            this.Excludes = new List<string>();
        }

        public string RootId { get; set; }

        public List<string> Excludes { get; private set; }

        public RawArticle ReadAllArticle()
        {
            Console.WriteLine("read article tree...");
            var root = ReadArticleSchema();

            //reset total count
            this._currentContentIndex = 0;
            this._totalCount = root.CountAll();

            Console.WriteLine("reading article tree has completed, start reading html content of each file...");
            this.ReadContent(root);

            return root;
        }

        protected virtual RawArticle ReadArticleSchema()
        {
            var root = new RawArticle
            {
                Id = this.RootId,
                Level = 1,
            };
            this.ReadRootArticleTitle(root);

            this.ReadArticleRecur(root);

            return root;
        }

        private void ReadRootArticleTitle(RawArticle root)
        {
            this.ReadContentSingle(root);

            var match = Regex.Match(root.RawHtml, @"<title>(?<title>[^<]+)</title>");
            if (match.Success)
            {
                root.Title = match.Groups["title"].Value;
            }
        }

        private string _language;

        private string GetArticleUrl(string id)
        {
            if (_language == null)
            {
                _language = ConfigurationHelper.GetAppSettingOrDefault("Language");
            }
            if (string.IsNullOrEmpty(_language))
            {
                return string.Format("http://msdn.microsoft.com/library/{0}.aspx", id);
            }
            else
            {
                return string.Format("http://msdn.microsoft.com/{1}/library/{0}.aspx", id, _language);
            }
        }

        private void ReadArticleRecur(RawArticle parent)
        {
            string url = GetArticleUrl(parent.Id) + "?toc=1";

            Console.WriteLine("read children for " + parent.Title + " ...");
            var json = Download(url);
            if (string.IsNullOrWhiteSpace(json)) return;
            try
            {
                var items = json.Split(new string[] { "},{" }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var item in items)
                {
                    var childId = Regex.Match(item, @"(?<id>\w+)(\([^\)]+\))?\.aspx").Groups["id"].Value;
                    if (string.IsNullOrEmpty(childId))
                    {
                        //throw new NotSupportedException(string.Format("Can't parse json from url :'{0}'!", url));
                        return;
                    }

                    if (!this.Excludes.Contains(childId))
                    {
                        var childTitle = Regex.Match(item, @"Title"":""(?<title>[^""]+)").Groups["title"].Value;

                        var child = new RawArticle
                        {
                            Id = childId,
                            Title = childTitle,
                            Level = parent.Level + 1,
                        };
                        this.ReadArticleRecur(child);

                        parent.Children.Add(child);
                    }
                }
            }
            catch (Exception ex)
            {
                //throw new InvalidProgramException("error on parse json，may be msdn web site has been updated.", ex);
            }
        }

        private void ReadContent(RawArticle tree)
        {
            this.ReadContentSingle(tree);

            foreach (var child in tree.Children)
            {
                this.ReadContent(child);
            }
        }

        protected virtual void ReadContentSingle(RawArticle item)
        {
            if (string.IsNullOrEmpty(item.RawHtml))
            {
                if (string.IsNullOrEmpty(item.Title)) { item.Title = "root article."; }

                Console.WriteLine(string.Format("read content for {0}({1}/{2}) ...", item.Title, this._currentContentIndex++, this._totalCount));

                string url = GetArticleUrl(item.Id);
                item.RawHtml = Download(url);
            }
        }

        protected string Download(string url)
        {
            try
            {
                return _client.DownloadString(url);
            }
            catch (WebException e)
            {
                //throw new NotSupportedException("error on downloading this page: " + url, e);
                return "";
            }
        }
    }
}